/**
 * @author renato
 * Created on 22/09/2005
 *
 * This class is used in the destination machine to receive the traffic packages.
 * It is also responsible for calculating the delay, jitter and packet loss.
 * It is created by trafficManager.
 *
 */
package br.unb.core.traffic;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

import br.unb.core.dispatcher.Dispatcher;
import br.unb.core.dispatcher.TrafficDispatcher;
import br.unb.utils.Console;
import br.unb.utils.ConsoleInterface;
import br.unb.utils.LogFile;
import br.unb.utils.LogPrinter;
import br.unb.utils.Resource;

import cgl.narada.sync.SyncTimestamp;
import javax.swing.text.DefaultStyledDocument;

/****************** Main code for the receiver machine ***********************/
public class ReceiverController implements Runnable, ConsoleInterface {

    private Traffic traffic;
    private boolean isRunning;
    private TrafficDatagramPacket tdPacket;
    private int packetNum;
    private SyncTimestamp receiveTimestamp;
    private Thread receiverThread;
    private LogPrinter timestampPrinter, dumpPrinter;
    private DatagramSocket trafficSocket;
    private int MAX_LENGTH = 8192;

    /**
     *
     */
    public ReceiverController(Traffic td) throws SocketException {
        super();
        /*Resource.printError(
        "Receiver machine: init()! "
        + td.getName());//debug
         */
        traffic = td;
        trafficSocket = new DatagramSocket(traffic.getDstPort());
        try {
            timestampPrinter = new LogPrinter(new LogFile(
                    LogFile.Type.RECEIVER,
                    traffic.getName()));
            dumpPrinter = new LogPrinter(new LogFile(
                    LogFile.Type.DUMP,
                    traffic.getName()));
        } catch (Exception e) {
            printError("ERROR_OPENING_LOG_FILE_ON_RECEIVER");
        }
        this.start();//dont wait any longer, begin receiving right now!
    }

    /******************
     * Methods below will be called from AWT-EventQueuer
     * start()
     * stop()
     * They are used to manage the running thread using shared variable isRunning
     ******************/
    public void start() {
        if (isRunning) {
            return;
        }
        /*printError(
        "Receiver machine: start()!\n"
        + getString("RECEIVING")
        + traffic.getName() + ". "
        + getString("PORT_USED")
        + traffic.getDstPort());//debug
         */
        isRunning = true;
        receiverThread = new Thread(this, "ReceiverController");
        receiverThread.setPriority(Thread.MAX_PRIORITY - 1);
        receiverThread.start();//calls this.run()
    }

    public void stop() {
        /*printError(
        "Receiver machine: stop()! "
        + Resource.getString("END_OF_RECEPTION")
        + traffic.getName()
        + ". "
        + Resource.getString("PORT_RELEASED")
        + traffic.getDstPort());//debug
         */
        isRunning = false;
        trafficSocket.close();
        try {
            timestampPrinter.close();
            dumpPrinter.close();
        } catch (Exception e) {
            printError("ERROR_CLOSING_LOG_FILE_ON_RECEIVER");
        }
        receiverThread.interrupt();
        Dispatcher.getInstance().sendReceiverFinishedAck(traffic);
    }

    public boolean isRunning() {
        if (isRunning) {
            return true;
        } else {
            return false;
        }
    }

    public Traffic getTraffic() {
        return traffic;
    }

    /*********************************************************
     * Methods below will run in a new Thread (spawnned from AWT-eventQueue)
     * run() 
     *  -receive();
     *   -addQoSParameter();
     *   -printToFiles();
     *********************************************************/
    public void run() {
        long pauseTime = 0;
        int countIoException = 0;
        /*
        receiverThread.printError(
        "Receiver machine: run()! : "
        + traffic.getName() + ". ");//Debug
         */
        while (isRunning) {
            try {
                this.receive();
                countIoException = 0;
            } catch (IOException e) {
                countIoException++;
                if (countIoException > 5) {
                    Console.showMessageDialog(
                            Resource.getString("EXCEPTION_GENERATED")
                            + e.getMessage());
                    this.stop();
                }
            }
            if (isRunning && tdPacket.getPacketNum() < 0) {
                this.stop();
            } else if (isRunning) {
                this.addQoSParameter();
                this.printToFiles();
            }
        }
        println("ReceiverController.run(): Normal exit");
    }

    /********************** Receiver specifc methods **************************/
    /**
     * receives a package
     */
    private void receive() throws IOException {
        DatagramPacket receivePacket = new DatagramPacket(new byte[MAX_LENGTH], MAX_LENGTH);
        trafficSocket.receive(receivePacket);
        receiveTimestamp = new SyncTimestamp();
        tdPacket = new TrafficDatagramPacket(receivePacket);
    }

    /**
     * Adds a set of QoS parameters to an unread vector
     */
    public void addQoSParameter() {
        if (TrafficDispatcher.useQos) {
            //printError(".");
            TrafficDispatcher.qosController.addQosToTraffic(
                    tdPacket.getPacketNum(),
                    tdPacket.getPacketLen(),
                    tdPacket.getTimestamp().getTimeInMicroSeconds(),
                    receiveTimestamp.getTimeInMicroSeconds(),
                    traffic.getName());
        }
    }

    void printToFiles() {
        try {
            timestampPrinter.logEntry(
                    tdPacket.getPacketNum(),
                    (tdPacket.getPacketLen() - 16),
                    tdPacket.getTimestamp().getTimeInMicroSeconds(),
                    receiveTimestamp.getTimeInMicroSeconds());

            if (tdPacket.getPacketNum() > 0
                    && traffic.getStart() < 0) {
                //write the data received to a file.
                dumpPrinter.write(
                        tdPacket.getData(),
                        16,
                        tdPacket.getPacketLen() - 16);
            }
        } catch (IOException ioe) {
            printError("LOGGING_ERROR_ON_RECEIVER");
        } catch (Exception e) {
            printError("LOGGING_ERROR_ON_RECEIVER");
        }
    }
    /**Console interface methods*/
    public static DefaultStyledDocument doc = null;

    @Override
    public void printError(String s) {
        Console.println(s, System.err, doc);
    }

    @Override
    public void println(String s) {
        Console.println(s, System.out, doc);
    }

    @Override
    public void setDocument(DefaultStyledDocument doc) {
        ReceiverController.doc = doc;
    }
}
